package client;

import java.rmi.*;
import java.util.ArrayList;

import javax.sound.sampled.LineUnavailableException;

import repository.Note;
import repository.Repository;

public class Player implements Runnable {

	private Repository musicRepo;
	private ArrayList<ArrayList<Note>> sheetMusic;
	private boolean playing;
	private int songLength;
	private long prevBarTime;
	private long prevBarLength;
	private long prevBarAvg;
	private long curBarTime;
	private long curBarLength;
	private int currentBarNumber;
	private int initialised;
	private int musicianNumber;

	public Player(Repository repo, int musician) {
		musicRepo = repo;
		sheetMusic = new ArrayList<ArrayList<Note>>();
		prevBarLength = 0;
		curBarLength = 0;
		prevBarAvg = 0;
		initialised = 0;
		musicianNumber = musician;
	}

	public void run() {
		try {
			sheetMusic = musicRepo.getMusic(musicianNumber);
			songLength = musicRepo.getSongLength();
			System.out.println(sheetMusic);
		} catch (RemoteException e) {
			System.err.println(e.getMessage());
		}

		// loop
		// figure out what time it is
		// play whatever is next
		playing = true;
		for (/* ArrayList<Note> bar : sheetMusic */;;) {
			long currentBarLength = getPrevBarAvg();
			long shortestUnit = currentBarLength / 32;
			int cb = getCurrentBar();
			if (initialised > 0 && System.currentTimeMillis() - getPrevBarTime() > 20000)
			//if (System.currentTimeMillis() - getPrevBarTime() > 20000)
				break;
			if (cb < 0)
				continue;

			for (Note n : sheetMusic.get(cb)) {
				int note = n.getNote();
				int octave = n.getOctave();
				int length = (int) (n.getDuration() * shortestUnit);
				double volume = 1.0;

				if (!n.isRest()) {
					try {
						System.out
								.println(n.getDuration() + " " + shortestUnit);
						System.out.printf("%d %d %d %f\n", note, octave,
								length, volume);
						Oscillator.playNote(note, octave, length, volume);
					} catch (IllegalArgumentException e) {
						System.err.println(e.getMessage());
					} catch (LineUnavailableException e) {
						System.err.println(e.getMessage());
					}
				}
			}
			if (cb == songLength - 1) { // we just played the last bar! lets
										// stop now
				playing = false;
				break;
			}
		}
		// if interrupted, die (will be replaced with a Conductor)
		// if conductor disappears, interrupt Monitor to perform new election

	}

	public synchronized void syncTime(int curBar) {
		if (initialised == 0) {
			prevBarTime = System.currentTimeMillis();
			initialised = 1;
		} else if (initialised == 1) {
			curBarTime = System.currentTimeMillis();
			prevBarLength = curBarTime - prevBarTime;
			prevBarAvg = prevBarLength;
			prevBarTime = curBarTime;

			initialised = 2;
		}

		long curTime = System.currentTimeMillis();
		long newLength = curTime - prevBarTime;

		curBarTime = curTime;
		curBarLength = newLength;

		// Exponential moving average over bar lengths
		long alpha = 2;
		long newAvg = (prevBarLength / alpha) + (prevBarAvg / alpha);

		prevBarTime = curBarTime;
		prevBarLength = curBarLength;
		prevBarAvg = newAvg;

		currentBarNumber = curBar;

		System.out.println("Time updated: current bar: " + curBar);
	}

	public synchronized long getPrevBarAvg() {
		return prevBarAvg;
	}

	public synchronized int getCurrentBar() {
		return currentBarNumber;
	}
	
	public synchronized long getPrevBarTime() {
		return prevBarTime;
	}

	public synchronized boolean isPlaying() {
		return playing;
	}

}
